local filename = "noderequest.lua"
local skynet = require "skynet"
local cluster = require "skynet.cluster"
require "skynet.manager"

local servicepoolmng = require "incrservicepoolmng"
local configdao = require "configdao"
local timetool = require "timetool"
local filelog = require "filelog"
local tabletool = require "tabletool"
local List = require "list"

local json = require "cjson"
json.encode_sparse_array(true, 1, 1)

require "imenum"

local CMD = {}
local service = nil

--是否进桌中
local is_entertalk = false

-----------------

--
--创建游戏桌
--
local function create_game_talk()
    -- level = level or 1
    -- table_conf = table_conf or gametable_conf[level]
    -- if not table_conf then
    --     return false
    -- end

    if not roledata then
        roledata = {
            info = { 
                rolename = "root",  --名字
                level = 0,          --等级
		        logo = "1",         --头像
		        sex = 2,            --性别
                rid = 22222222,
            },
            money = {
                diamond = 999999999,
                coin = 999999999,
                lock_coin = 0,
                lock_diamond = 0,
             },
            table_persist = 1800,   --保留30分钟
            ip_addr = "",
        }
    end

    local func = service.CMD.createtalk
    local msgbody = {}
    local response = func(roledata.info.rid,roledata,msgbody, true)
    if not response or response.retcode then
        return false
    end

    return response
end

--
--获得一个空闲桌
--
local function get_one_idle_talk()
    local idle_talk_list = service.game_talk_list.idle_talk

    --从队首取一个空闲桌
    local talk_index = nil
    if service.game_talk_list.idle_talk and service.game_talk_list.idle_talk:get_size() > 0 then
        while true do
            talk_index = service.game_talk_list.idle_talk:pop_left()
            if not talk_index or service.index_to_talk[talk_index] then
                break
            end
        end
    end
    --filelog.sys_error("--------get_one_idle_talk-----talk_index---", talk_index)
    --没有空闲桌，创建新桌子
    if not talk_index then
        local result = create_game_talk()
        ---filelog.sys_error("---------result-----", result)
        if not result then
            return false
        end
        --idle_talk_list = idle_talk_list or List:new()
        service.game_talk_list.idle_talk:push_left(result.talk_index)
        --service.game_talk_list.idle_talk = idle_talk_list
        return get_one_idle_talk()
   end

    --检测桌子是否可进入
    local talkitem = service.index_to_talk[talk_index]
    local eseat_num = skynet.call(talkitem.talk_service, "lua", "cmd", "check_talkstate")
    ---filelog.sys_error("-------eseat_num-----", eseat_num, talk_index, service.game_talk_list.idle_talk:get_size())
    if not eseat_num then           --桌子不存在
        return get_one_idle_talk()
    elseif eseat_num <= 0 then      --桌子已满
        --service.game_talk_list.idle_talk:push_right(talk_index)
        return -1
    else
        --service.game_talk_list.idle_talk:push_right(talk_index)
        return talk_index
    end

end

------------------------

function CMD.init(caller)
	service = caller -- 这里的caller，是router
    service.wait_entertalk = { }   --等待进桌队列,防止多次进桌
end

function CMD.request(msg,...)
    local f = CMD[msg]
    if not f then
        return { retcode = EErrCode.ERR_INVALID_REQUEST,retstr = "消息不存在！"}
    end

    return f(...)
end

--------------------------------------

--
-- 生成一个桌子号，随机码
-- 这里暂时是本节点生成，以后要去 hall 生成,带入的两个参数，也是准备传到 hallnode 的
--
local function gen_table_code(table_index, create_rid, table_conf)
	-- local node 生成

	-- 去hall节点生成code，同时 hall节点保存了code -> index 对应关系
	-- hall节点也可以是gata节点
	local status,newcode = skynet.pcall(cluster.call,"hallnode", ".router", "create_table_code",skynet.getenv("nodename"), table_index, create_rid, table_conf)
    if not status then return end
	return newcode
end

-------------------

--
-- 初始化talk池
--
function CMD.init_talk_pool(nodeconf)
	service.idle_talk_mng = servicepoolmng:new({}, {service_name="talk", service_size=nodeconf.tablesize, incr=nodeconf.tableinrc})
end


function CMD.createtalk(rid, roledata, msgbody, islocal)
    --客户端不能建桌
    if not islocal then
        return {}
    end

    --设置停服标记后，不能创建桌子
    if service.gameend_flag then
        return {retcode = EErrCode.ERR_INVALID_REQUEST, retstr = "服务器维护中"}
    end

    --进桌中，不能建桌
    if service.wait_entertalk[rid] or service.role_map[rid] then
        return 
    end

	--检查参数
    local response = { }
    local table_conf = {}
    local playermoney = roledata.money or { }
    local curmoney = playermoney.diamond or 0

    local talk_service = service.idle_talk_mng:create_service()
	if talk_service == nil then
		filelog.sys_error("create_table roomsvr's idle_talk_mng not enough talk_service!")
		return {retcode = EErrCode.ERR_SYSTEM_ERROR,retstr = "system error!"}
	end

	-- msgbody 里面包含了创建房间的配置， 具体在 proto 里的 tablecfg 里面定义
	-- 先生成一个table_service, 然后生成 code， 然后生成对应关系，
	-- 然后把 msgbody里的conf 传入进去 然后再去初始化这个table_service
	local talk_index = service.talk_index
	-- local table_code = gen_table_code(service.table_index, rid, msgbody.table_conf)
 --    if not table_code then
 --        skynet.call(table_service.service, "lua", "cmd", "kill_service")
 --        return	{retcode = EErrCode.ERR_SYSTEM_ERROR,retstr = "系统错误！"}
 --    end
   
	service.index_to_talk[talk_index] = {talk_service = talk_service.service}
	service.talk_index = service.talk_index + 1

    local tradeid = service:get_tradeid()
	local attachconf = {
        index = talk_index, 
		rid = rid,
		rolename = roledata.info.rolename,
        logo = roledata.info.logo,
        talk_service = talk_service.service,
        tradeid = tradeid,
        talk_persist = roledata.table_persist,  --桌子没人保留时间
    }
    local conf = {
        max_player_num = 1,
    }
	-- 如果失败，回滚刚才的操作（不过这个失败可能性很小，因为这个操作，只是对一些对象赋值
    service.wait_entertalk[rid] = true
	local result = skynet.call(talk_service.service, "lua", "start", conf, attachconf)
    filelog.sys_info("talk_index:",talk_index,talk_service.service)
    service.wait_entertalk[rid] = nil

	return {talk_index = talk_index, nodename = skynet.getenv("nodename")}
end

--
-- 进入桌子操作的时候，用户还不在某张桌子上，所以不能在table service处理，所以还需要在router里面处理
-- 这里做了个兼容，可以使用 table_index , 也可以使用table_code ，如果有 table_index 则先用index直接进入
-- 如果没有index，只有 table_code ,则用table_code 找到对应的index ,再进入。
--
function CMD.enter_gametalk(rid, roledata, msgbody, request)
    --设置停服标记后，不能进入桌子
    if service.gameend_flag then
        return {retcode = EErrCode.ERR_INVALID_REQUEST, retstr = "服务器维护中"}
    end

    --进桌中
    if service.wait_entertalk[rid] or service.role_map[rid] then
        return 
    end

    roledata.money = roledata.money or {}
	local talk_index = nil
	if msgbody.talk_index then
		talk_index = msgbody.talk_index
	else
		return {retcode = EErrCode.ERR_INVALID_TABLE_INDEX, retstr="无效会话"}
	end

	local talk_service = nil
	if service.index_to_talk[talk_index] then 
		talk_service = service.index_to_talk[talk_index].talk_service
	else
        --通知gate，玩家离桌
        service:unforward(rid, talk_index, roledata.money.coin)
		return {retcode = EErrCode.ERR_INVALID_TABLE_INDEX, retstr="房间不存在"}
	end

    service.wait_entertalk[rid] = true
	local status,result = skynet.pcall(skynet.call,talk_service, "lua", "entertalk", rid, roledata, request)
    service.wait_entertalk[rid] = nil

	-- 如果成功,只有正确结构返回，没有 retcode，所以要通知gate去更改状态。
	if result and result.retcode == nil then
		service:forward(rid, talk_service, talk_index, roledata.money.coin)
	end

	return result
end

function CMD.auto_enter_gametalk(rid, roledata, msgbody, request)
    --设置停服标记后，不能进入桌子
    if service.gameend_flag then
        return {retcode = EErrCode.ERR_INVALID_REQUEST, retstr = "服务器维护中"}
    end

    --进桌中
    if service.wait_entertalk[rid] or service.role_map[rid] then
        return 
    end

    roledata.money = roledata.money or {}
    local talk_index = nil
    if msgbody.talk_index then
        talk_index = msgbody.talk_index
    else
        return {retcode = EErrCode.ERR_INVALID_TABLE_INDEX, retstr="无效会话"}
    end

    local talk_service = nil
    if service.index_to_talk[talk_index] then 
        talk_service = service.index_to_talk[talk_index].talk_service
    else
        --通知gate，玩家离桌
        service:unforward(rid, talk_index, roledata.money.coin)
        return {retcode = EErrCode.ERR_INVALID_TABLE_INDEX, retstr="房间不存在"}
    end

    service.wait_entertalk[rid] = true
    local status,result = skynet.pcall(skynet.call,talk_service, "lua", "entertalk", rid, roledata, request)
    service.wait_entertalk[rid] = nil

    -- 如果成功,只有正确结构返回，没有 retcode，所以要通知gate去更改状态。
    if result and result.retcode == nil then
        service:forward(rid, talk_service, talk_index, roledata.money.coin)
    end

    return result
end

--
--进入聊天服务
--
function CMD.entertalk(rid, roledata, msgbody)
    --设置停服标记后，不能进入桌子
    if service.gameend_flag then
        return {retcode = EErrCode.ERR_INVALID_REQUEST, retstr = "服务器维护中"}
    end

    --进桌中
    if service.wait_entertalk[rid] or service.role_map[rid] then
        return 
    end

    --进桌中等待
    local cnt = 0
    while is_entertalk do
        cnt = cnt + 1
        if cnt > 40 then
            return {retcode = EErrCode.ERR_SYSTEM_ERROR, retstr = "网络繁忙，请稍后重试"}
        end
        skynet.sleep(5)
    end

    --设置进桌标记
    is_entertalk = true

    --从桌子列表中找一个有空位的桌子
    local _,talk_index = skynet.pcall(get_one_idle_talk)
    is_entertalk = false
    if not talk_index then
        return {retcode = EErrCode.ERR_SYSTEM_ERROR, retstr = "系统错误"}
    elseif talk_index == -1 then
        return {retcode = EErrCode.ERR_TABLE_FULL, retstr = "房间人数已满"}
    end

    return CMD.enter_gametalk(rid, roledata, {talk_index = talk_index}, msgbody)
end

function CMD.auto_entertalk(rid, roledata, msgbody)
    --设置停服标记后，不能进入桌子
    if service.gameend_flag then
        return {retcode = EErrCode.ERR_INVALID_REQUEST, retstr = "服务器维护中"}
    end

    --进桌中
    if service.wait_entertalk[rid] or service.role_map[rid] then
        return {retcode = EErrCode.ERR_INVALID_REQUEST, retstr = ""}
    end

    --从桌子列表中找一个有空位的桌子
    local _,talk_index = skynet.pcall(get_one_idle_talk)
    if not talk_index then
        return {retcode = EErrCode.ERR_SYSTEM_ERROR, retstr = "系统错误"}
    elseif talk_index == -1 then
        return {retcode = EErrCode.ERR_TABLE_FULL, retstr = "房间人数已满"}
    end

    return CMD.auto_enter_gametalk(rid, roledata, {talk_index = talk_index}, msgbody)
end

--
--玩家断开连接
--
function CMD.disconnect(rid, roledata)
    -- local role_map_data = service:get_role_data(rid)
    -- if role_map_data then
    --     local talk_index = role_map_data.talk_index
    --     service:unforward(rid, talk_index)
    -- end
    return true
end

--
--断线重连
--
function CMD.reentertalk(rid, roledata, request)
    ---filelog.sys_error("--------noderequest.reentertalk---------", rid, roledata, request)
    local notciemsg = { }
    notciemsg.retcode = EErrCode.ERR_INVALID_REQUEST
    notciemsg.retstr = "不在桌子内！"
    service:unforward(rid, request.talk_index, roledata.money.diamond)
    return notciemsg
end

--
--获得场次桌子信息
--
function CMD.getlevellist()
    -- local levellist = { }

    -- for i,conf in pairs( gametable_conf) do
    --     levellist[i] = {
    --         level_type 	            = i,	                    --场次类型 1初级 2中级 3高级
	   --      min_carry_amount  		= conf.min_carry_amount,	--最小携带量
	   --      max_carry_amount		= conf.max_carry_amount, 	--最大携带量
	   --      up_banker_money		    = conf.up_banker_money,	    --上庄需要金币
	   --      conf_intro              = json.encode(conf),        --桌子配置                       
    --     }

    --     --该场次当前人数
    --     levellist[i].cur_total_player =  service.diff_level_players[i] or 0

    -- end
    
    -- return {levellist = levellist}
end

function CMD.getalltalkrids(rid, roledata, request)
    local responsemsg = {}
    ---获取所有和玩家rid聊天的rid
    local alltalkrids = {}
    local status, result = skynet.pcall(skynet.call, ".talkcache", "lua", "get_alltalkrids", rid)
    ---filelog.sys_error("------get_alltalkrids-----status, result-------", rid, status, result)
    if not status or not result or next(result) == nil then

    else
        for index, talkrid in pairs(result) do
            table.insert(alltalkrids, talkrid)
        end
    end
    responsemsg.alltalkrids = alltalkrids
    return responsemsg
end

function CMD.leavetalk(rid, roledata, request)
    local responsemsg = {}
    ---skynet.send(".router","lua","leave_talk",nil,talkobj.rid,talkobj.index)
    service:unforward(rid, 0, 0)
    skynet.send(".talkcache", "lua", "clearcache", rid)
    return responsemsg
end

return CMD